package com.mapforge.selection;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Iterator;

public class ContinuousSelection implements Selection, Cloneable {

	private Rectangle selectedArea;

	public ContinuousSelection() {
		this(new Rectangle(0, 0, 0, 0));
	}

	public ContinuousSelection(Rectangle rectangle) {
		this.selectedArea = new Rectangle(rectangle);
	}

	/**
	 * Returns a deep copy of the area this selection encompasses.
	 * 
	 * @return
	 */
	public Rectangle getBoundingRectangle() {
		return new Rectangle(selectedArea);
	}

	public void setOrigin(int x, int y) {
		selectedArea.x = x;
		selectedArea.y = y;
	}

	public boolean add(Selection other) {
		if (selectedArea.isEmpty()) {
			this.selectedArea = (Rectangle) other.getBoundingRectangle()
					.clone();
			return true;
		} else {
			throw new IllegalStateException(
					"Continuous selection cannot add specified selection");
		}
	}

	@Override
	public boolean contains(int x, int y) {
		return selectedArea.contains(x, y);
	}

	public boolean contains(Point p) {
		return contains(p.x, p.y);
	}

	/**
	 * Performs the intersection of this selection with another specified
	 * selection. If the specified selection isn't an instance of
	 * {@link ContinuousSelection} an {@link IllegalArgumentException} will be
	 * thrown, as intersections with other types of selections might not result
	 * in a valid {@link ContinuousSelection}.
	 */
	public boolean intersect(Selection other) {
		if (other instanceof ContinuousSelection) {
			Rectangle intersection = other.getBoundingRectangle().intersection(
					selectedArea);
			// the two selectin's bounding rectangle's intersect
			if (!intersection.isEmpty()) {
				this.selectedArea = intersection;
				return true;
			}
			return false;
		} else {
			throw new IllegalArgumentException(
					"Continuous Selections can only intersect other Continuous Selections");
		}
	}

	@Override
	public Iterator<ComparablePoint> iterator() {
		return new RectangularIterator((Rectangle) selectedArea.clone());
	}

	@Override
	public boolean subtract(Selection other) {
		throw new UnsupportedOperationException(
				"Cannot subtract from ContinuousSelection");
	}

	public Object clone() {
		return new ContinuousSelection((Rectangle) selectedArea.clone());
	}

	@Override
	public void clearSelection() {
		this.selectedArea.setBounds(0, 0, 0, 0);
	}

	@Override
	public Iterator<ComparablePoint> iterator(Rectangle rectangle) {
		Rectangle intersection = this.selectedArea.intersection(rectangle);
		return new RectangularIterator(intersection);
	}

	@Override
	public boolean intersects(Selection other) {
		if (other instanceof ContinuousSelection) {
			ContinuousSelection selection = (ContinuousSelection) other;
			return this.selectedArea.intersects(selection.selectedArea);
		} else {
			Iterator<? extends Point> it = other.iterator();
			while (it.hasNext()) {
				if (selectedArea.contains(it.next())) {
					return true;
				}
			}
			return false;
		}
	}

	@Override
	public Selection addition(Selection other) {
		DiscontinuousSelectionCollection selection = (DiscontinuousSelectionCollection) this
				.clone();
		for (ComparablePoint p : other) {
			selection.addPoint(p);
		}
		for (ComparablePoint p : this) {
			selection.addPoint(p);
		}
		return selection;
	}

	@Override
	public Selection subtraction(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection intersection(Selection other) {
		Rectangle intersection = selectedArea.intersection(other
				.getBoundingRectangle());
		// bounding rectangle of continuous selection
		if (other instanceof ContinuousSelection) {
			return new ContinuousSelection(intersection);
		} else {
			DiscontinuousSelectionCollection selection = (DiscontinuousSelectionCollection) this
					.clone();
			RectangularIterator it = new RectangularIterator(intersection);
			ComparablePoint p;
			// iterate through selections' intersection
			while (it.hasNext()) {
				p = it.next();
				if (contains(p.x, p.y) && other.contains(p.x, p.y)) {
					selection.addPoint(p);
				}
			}
			return selection;
		}
	}

	@Override
	public boolean isEmpty() {
		return selectedArea.isEmpty();
	}
}